package fr.grassaud.hmvc.spring.session;

import java.lang.reflect.Field;

import javax.servlet.ServletContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import fr.grassaud.hmvc.info.TriadInfo;
import fr.grassaud.hmvc.session.Triad;
import fr.grassaud.hmvc.session.TriadListener;
import fr.grassaud.hmvc.spring.component.SpringBean;
import fr.grassaud.hmvc.util.HMVCIntrospector;

/**
 * Triad listener use to inject automatically the spring bean using annotation
 * <code>SpringBean</code> in triad components. The injection is done after that
 * the triad is created. Note that spring context should be load before the
 * creation of the <code>HMVCSessionFactory</code>
 * 
 * @author vgrassaud
 * 
 */
public class SpringTriadListener implements TriadListener {

    /**
     * The Spring Application context
     */
    private final ApplicationContext context;

    private Logger logger = LoggerFactory.getLogger(SpringTriadListener.class);

    /**
     * Creates the listener with the Spring Application context
     * 
     * @param context
     *            the spring application context to use
     */
    public SpringTriadListener(ApplicationContext context) {
	this.context = context;
    }

    /**
     * Creates the listener from the Servlet context. This method tries to get
     * the spring Application Context from the servlet context
     * 
     * @param context
     *            the servlet context to instrospect in order to retrieve the
     *            spring application context
     */
    public SpringTriadListener(ServletContext context) {
	this.context = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
    }

    @Override
    /**
     * Inject the spring beans when the triad is created.
     */
    public void onTriadCreated(Triad triad) {
	if (context != null) {
	    TriadInfo info = triad.getTriadInfo();
	    injectSpringBrean(triad.getController(), info.getControllerClass());
	    if (info.getModelClass() != null && triad.getModel() != null ) {
		injectSpringBrean(triad.getModel(), info.getModelClass());
	    }
	    injectSpringBrean(triad.getView(), info.getViewClass());
	}

    }

    private void injectSpringBrean(Object component, Class<?> componentClass) {
	for (Field f : HMVCIntrospector.getAllFields(componentClass)) {
	    if (f.isAnnotationPresent(SpringBean.class)) {
		f.setAccessible(true);
		try {
		    f.set(component, context.getBean(f.getType()));
		} catch (BeansException e) {
		    logger.error("ERROR", e);
		} catch (IllegalArgumentException e) {
		    logger.error("ERROR", e);
		} catch (IllegalAccessException e) {
		    logger.error("ERROR", e);
		}
	    }
	}
    }

    @Override
    /**
     * Does nothing 
     */
    public void onTriadInstanciated(Triad triad) {
	// do nothing
    }

    /**
     * Returns the spring application context used by this listener
     * 
     * @return the spring application context used by this listener
     */
    public ApplicationContext getContext() {
	return context;
    }

}
